三、核心神经网络推理引擎(taRuntime)
3.1 taRuntime 概述
关于网络模型的工作大致可以分为模型导入阶段和运行时(Runtime)阶段,本文档主要讨论运行时阶段,也就是与在板端执行推理有关的问题。
taRuntime 是 EA65xx 芯片 SDK 的一个重要组成模块,它在板端提供了一组 C 语言编程接口(API),用于支持板端应用程序基于 EA65xx NPU 硬件完成网络模型推理任务。
为了帮助您理解和使用 taRuntime 模块的接口,本章就一些重要概念和工作原理进行说明。由于篇幅限制,本章只重点讲解与接口设计和使用相关的内容,对于通识性的概念将不做展开说明。
taRuntime 提供的接口功能覆盖:
- 网络模型加载
- 模型信息提取
- 输入输出配置
- 执行模型推理
3.2 关于 Buffer Pool
如前所述,对于 NPU 推理而言,不论输入图像是 CPU 虚拟地址还是物理地址都需要映射成 NPU 虚拟地址才能被 NPU 使用,而映射过程总是要有一定开销的。
Pool/VB 机制
SDK 的媒体驱动(tasys)提供了一套 Pool/VB 机制(Video Buffer Pool),通过循环复用 Pool 中提供的 VB(Video Frame Buffer)可以减少内存分配和地址映射的次数,从而降低软件延迟。
工作原理
- taRuntime 接口内部维护了一个查找表
- 当外部提供图像的方式为物理地址时,接口内部首先试图从查找表获取对应的 NPU 虚拟地址
- 只有查表失败时才会发起地址映射操作
- 当外部提供的物理地址源于 Pool/VB 机制时,查表成功的概率极大,可以显著压缩地址管理开销
3.3 关于前处理
前处理定义
前处理(Pre-processing)指的是在执行模型推理之前,对输入数据(图像)进行以下操作,使其符合网络模型第一层对数据格式的要求:
- 缩放(Resize)
- 裁切(Crop)
- 颜色空间变换(Color Space Conversion)
- 存储格式和精度格式变换
前处理方案
硬件加速方案:
- SDK 提供的 taCV 和 taOpenCV 模块提供了丰富的接口,支持大多数常见的前处理需求
- 比如,taCV 模块提供基于芯片硬件单元完成的缩放、裁切、颜色空间变换等操作
软件处理方案:
- 对于某些芯片硬件单元不支持的操作,其算子是基于 CPU 算力实现
- 以 RGB24 图像转 INT8 张量格式为例,涉及像素位置重排和减 offset 操作,在 taOpenCV 中这类算子基于 CPU 算力实现,耗时较长
优化建议
模型移植优化:
建议在模型移植阶段就把这类算子合并到网络模型中,推理时利用 NPU 算力完成处理。
PPU 单元利用:
- EA65xx 芯片的 NPU 配属了 PPU(Pre-processing Unit)单元
- PPU 提供了一定的算子可编程能力
- 原厂可以基于 PPU 硬件开发新的算子并通过 taCV 模块暴露给用户使用
⚠️ 注意:
- NPU 单元与 PPU 单元只能分时工作,使用 PPU 算子会占用 NPU 算力。
3.4 关于后处理
后处理定义
后处理(Post-processing)一般是指对 NPU 输出的推理结果进行过滤从而获得最终结果。
典型后处理任务
以 YOLO 系列模型为例,典型的后处理任务包括:
- 从上万个待处理结果中去除冗余的检测框
- 删除置信度过低的检测框
- 对幸存结果进行排序获得 top-k 个候选框
- 根据预设的阈值筛选出最终的检测框
- 将类别索引 ID 映射回人类可理解的类别标签
后处理实现方式
CPU 处理原因:
- 涉及大量的复杂逻辑处理,运算性质不利于硬件实现
- 算法更新迭代速度快,后处理需求经常变化
推荐方案:
- 后处理步骤主要借助 CPU 算力完成
- taOpenCV 提供了丰富的专用算子
- 用户可以利用 taOpenCV 接口实现大部分后处理操作
3.5 关于多进程多线程
本模块的接口支持在多个进程、多个线程中调用,具体工作逻辑如下:
进程工作流程
对于一个进程来说,其工作流程可以分成三个阶段:
- 初始化阶段
- 推理阶段
- 反初始化阶段
一般做法是在推理阶段创建多个工作线程,每个线程负责一路视频流的推理。
多进程支持
当存在多个进程时:
- 为了避免在进程间协调接口调用时序,SDK 允许每个进程都独立地调用初始化和反初始化接口
- 实际上只有一次调用会实际生效(触发硬件初始化/反初始化流程)
- 其它无效的调用会被忽略,但不返回错误
多线程任务管理
在一个进程内部:
- 用户调用接口时需要提供一个 context 结构体
- context 的作用是帮助接口识别任务来源
- 来自多个线程的任务最终会被推送到由内核驱动维护的任务队列
- 硬件按照先到先服务的原则依次处理队列中的任务
3.6 taRuntime 模块开发
NN 推理概念
NN 推理(Neural Network Inference)指的是在开发板上运行一个 AI 推理程序,核心流程包括:
- 从 Linux 文件系统加载网络模型到内存中
- 提取推理指令流和权重参数
- 分配内存和计算资源
- 提交推理任务到 taRuntime
- 触发 EA65xx 芯片的 NPU 运行模型
- CPU 对 NPU 推理结果进行后处理
- 输出检测/识别结果
开发流程
- 模型准备:请算法工程师提供能在 EA65xx 板端运行的模型文件
- 程序开发:应用开发工程师参考 sample 编写程序
- 接口调用:调用 taRuntime 库的 API 接口加载和运行模型文件
- 前后处理:调用其它 SDK 模块提供的 API 接口获取图像及进行前后处理
- 部署调试:将编译好的程序拷贝到板端调试运行
示例代码
// taRuntime API 基本使用流程
// 初始化
ta_runtime_init();
// 创建上下文
ta_runtime_context context;
// 加载模型:从文件系统加载到系统内存,再读入 NN 域
ta_runtime_load_model_from_file(&context, file_path, 0);
// 设置模型输入
taconn_input_t input;
ta_runtime_set_input_cva(&context, input_count, input);
// 设置模型推理的输出 buffer
taconn_buffer_t *output;
ta_runtime_create_buffer(&context, output_size, output);
ta_runtime_set_output(&context, output_count, output);
// 运行网络
ta_runtime_run_network(&context);
// 确保 CPU 读到最新数据
ta_runtime_invalidate_buffer(&context, output);
// 读取 buffer 数据后,释放内存
ta_runtime_destroy_buffer(&context, output);